package com.hougong.im.transport;

import com.hougong.im.common.GlobalCode;
import com.hougong.im.utils.ProtocolUtils;
import com.hougong.im.utils.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
/**
 * 起始位	包长度	消息流水号	协议版本	消息类型	业务类型	数据区	校验码
 * 1字节	   4字节	    4字节	    1字节	1字节	2字节		    1字节
 *  起始位位0x6C   若除了起始位其他地方有6C需要转义 规则如下
 *    	6C =====> 6D02
 *    	6D =====> 6D01
 *  	转义处理过程如下：
 * 发送消息时：消息封装——>计算并填充校验码——>转义；
 * 接收消息时：转义还原——>验证校验码——>解析消息；
 */

/**
 * 解码器
 * 1.先转义还原 2 校验验证码 3 解析封装成pojo 内部传输
 */
public class ProtocolDecoder extends ByteToMessageDecoder {
    private static final Logger LOGGER=LoggerFactory.getLogger(ProtocolDecoder.class);
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int readIndex = in.readerIndex();
        //获取原始数据字节数组
        byte[] receiveDate = new byte[in.readableBytes()];
        in.readBytes(receiveDate);
        in.readerIndex(readIndex);
//        LOGGER.info("=============接受的数据 "+ctx.channel().remoteAddress()+"==========\n"+StringUtils.bytesToHexString(receiveDate));
        //转义的数据 还原
        Map<Integer, Integer> convert_recv = new HashMap<>(4);  //记录转义过的位置
        byte[] convertData = unEscape(receiveDate, convert_recv);
//        for (Map.Entry<Integer,Integer> entry:convert_recv.entrySet()){
//            LOGGER.info("记录转义位置map key"+entry.getKey()+" value="+entry.getValue());
//        }
//        LOGGER.info("========还原的数据============="+StringUtils.bytesToHexString(convertData));
        ByteBuf byteBuf = Unpooled.wrappedBuffer(convertData);
        int count=0;
        while (byteBuf.readableBytes() >= BaseFrame.BASE_LENGTH) {
            int beginIndex;
            while (true) {
                beginIndex = byteBuf.readerIndex();
                byteBuf.markReaderIndex();
                in.readerIndex(getRecvDataIndex(beginIndex, convert_recv));
                in.markReaderIndex();
                if (byteBuf.readByte() == 0X6c) {
                    if (byteBuf.readByte() != 0x6c) {
                        byteBuf.resetReaderIndex();
                        break; //找到起始标志位
                    }
                    break;
                }
                byteBuf.resetReaderIndex();
                byteBuf.readByte();
                if (byteBuf.readableBytes() < BaseFrame.BASE_LENGTH) {
                    int nowIndex = getRecvDataIndex(byteBuf.readerIndex(), convert_recv);
                    in.readerIndex(nowIndex); //设置原始buf当前读的位置
                    byteBuf.release();  //删除还原数据的Buf
                    in.discardReadBytes();
                    return;
                }
            }
            byte start = byteBuf.readByte();
            int len = byteBuf.readInt();

            int streamId=byteBuf.readInt();
            byte version = byteBuf.readByte();
            byte messageType=byteBuf.readByte();
            short bizType=byteBuf.readShort();
            GlobalCode globalCode = GlobalCode.find(bizType);
            if(globalCode==null){
                //说明数据错误
                LOGGER.error("业务类型不对，数据错误，将其丢弃");
                byteBuf.release();
                in.release();
                return;
            }else {
                LOGGER.info("业务类型: "+globalCode.getRemark());
            }
            if(byteBuf.readableBytes()+4+1+1+2<len+1){
                //还有数据没来
                in.resetReaderIndex();
                byteBuf.release();
                return;
            }else {
                int bodyLen=len-(4+1+1+2);
                byte[] body = new byte[0];
                if(bodyLen>0){
                    body=new byte[bodyLen];
                    byteBuf.readBytes(body);
                }
                byte check=byteBuf.readByte();
                byte[] needCheckBytes=new byte[4+len];
                int i1 = byteBuf.readerIndex();
                int i=getRecvDataIndex(i1, convert_recv);
//                LOGGER.info("最终原始buf应该在的位置"+i);
                //设置原缓存数组的读的位置
                in.readerIndex(i);
                byteBuf.getBytes(beginIndex + 1, needCheckBytes);
                if(check==ProtocolUtils.computeCheck(needCheckBytes,0,needCheckBytes.length) && version == BaseFrame.VERSION){
                    BaseFrame baseFrame=new BaseFrame();
                    baseFrame.setStart(start);
                    baseFrame.setLength(len);
                    baseFrame.setStreamId(streamId);
                    baseFrame.setVersion(version);
                    baseFrame.setMessageType(messageType);
                    baseFrame.setBizType(bizType);
                    baseFrame.setBody(body);
                    baseFrame.setCheck(check);
                    out.add(baseFrame);
                }else {
                    LOGGER.info("接受的数据校验失败,前面数据丢弃");
                    in.discardReadBytes();
                }
            }
        }
    }

    /**
     * 返回对应原始数据的位置
     * 6d02ghg5646hh6d02
     * 6cgh5646hh6c
     *  0--
     *  0
     * @param convertIndex 还原后的数据位置
     * @param map          还原后的 ---> 还原前数据
     * @return
     */
    public int getRecvDataIndex(int convertIndex, Map<Integer, Integer> map) {
        Set<Integer> recv_index = map.keySet();
        int count = 0;
        for (Integer index : recv_index) {
            if (index == convertIndex) {
                return map.get(index) + 1;
            } else if (index < convertIndex) {
                count++;
            }
        }
        return convertIndex + count;
    }

    /**
     *  数据还原 6d02-->6c |  6d01-->6d
     * @param receiveData  接收的原始数据
     * @param  recv_conver key 转换后的位置  value 转化前原始数据位置
     * @return 转义还原后的数据
     */
    public static  byte[] unEscape(byte[] receiveData, Map<Integer,Integer>recv_conver){
        ArrayList<Byte> list=new ArrayList<>();
        for(int i=0;i<receiveData.length;i++){
            if(receiveData[i]==0x6d){
                if((i+1)<=receiveData.length-1){
                    if(receiveData[i+1]==0x02){
                        list.add((byte) 0x6c);
                        recv_conver.put(list.size()-1,i);
                        i++;
                        continue;
                    }else if(receiveData[i+1]==0x01){
                        list.add((byte) 0x6d);
                        recv_conver.put(list.size()-1,i);
                        i++;
                        continue;
                    }
                }
            }
            list.add(receiveData[i]);
        }
        byte[] byteArr=new byte[list.size()];
        for(int i=0;i<byteArr.length;i++){
            byteArr[i]=list.get(i);
        }
        list.clear();
        list=null;
        return  byteArr;
    }

}
